#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
天翼云盘文件操作模块
基于tem目录下的file_operations.py重构
"""

import os
import hashlib
import json
import time
import requests
from typing import Dict, List, Optional, Any, Union, BinaryIO
from urllib.parse import urlparse, parse_qs


class FileOperations:
    """天翼云盘文件操作类"""
    
    def __init__(self, client):
        """初始化文件操作
        
        Args:
            client: 天翼云盘客户端实例
        """
        self.client = client
        self.logger = client.logger
        self.session = client.session
    
    def get_files(self, folder_id: str = None, page_num: int = 1, page_size: int = 1000) -> Optional[Dict[str, Any]]:
        """获取文件列表
        
        Args:
            folder_id: 文件夹ID，None表示根目录
            page_num: 页码，从1开始
            page_size: 每页大小
        
        Returns:
            Dict[str, Any]: 文件列表数据
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        if folder_id is None:
            folder_id = "-11"  # 根目录
        
        url = "https://api.cloud.189.cn/listFiles.action"
        params = {
            'folderId': folder_id,
            'pageNum': page_num,
            'pageSize': page_size,
            'mediaType': 0,
            'iconOption': 5,
            'orderBy': 'lastOpTime',
            'descending': 'true'
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "GET")
        
        try:
            response = self.session.get(url, params=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    return {
                        'files': result.get('fileListAO', {}).get('fileList', []),
                        'folders': result.get('fileListAO', {}).get('folderList', []),
                        'total': result.get('fileListAO', {}).get('count', 0),
                        'page_num': page_num,
                        'page_size': page_size
                    }
                else:
                    self.logger.error(f"获取文件列表失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"获取文件列表请求失败: {e}")
        
        return None
    
    def get_file_info(self, file_id: str) -> Optional[Dict[str, Any]]:
        """获取文件详细信息
        
        Args:
            file_id: 文件ID
        
        Returns:
            Dict[str, Any]: 文件信息
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        url = "https://api.cloud.189.cn/getFileInfo.action"
        params = {
            'fileId': file_id
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "GET")
        
        try:
            response = self.session.get(url, params=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    return result.get('fileInfo', {})
                else:
                    self.logger.error(f"获取文件信息失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"获取文件信息请求失败: {e}")
        
        return None
    
    def get_download_url(self, file_id: str) -> Optional[str]:
        """获取文件下载链接
        
        Args:
            file_id: 文件ID
        
        Returns:
            str: 下载链接
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        url = "https://api.cloud.189.cn/getFileDownloadUrl.action"
        params = {
            'fileId': file_id,
            'dt': 1,
            'flag': 1
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "GET")
        
        try:
            response = self.session.get(url, params=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    return result.get('fileDownloadUrl')
                else:
                    self.logger.error(f"获取下载链接失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"获取下载链接请求失败: {e}")
        
        return None
    
    def download_file(self, file_id: str, save_path: str, filename: str = None) -> bool:
        """下载文件
        
        Args:
            file_id: 文件ID
            save_path: 保存路径（文件路径或目录路径）
            filename: 文件名（当save_path为目录时使用）
        
        Returns:
            bool: 下载是否成功
        """
        download_url = self.get_download_url(file_id)
        if not download_url:
            return False
        
        # 确定保存路径
        if os.path.isdir(save_path):
            if not filename:
                # 获取文件信息以确定文件名
                file_info = self.get_file_info(file_id)
                if not file_info:
                    self.logger.error("无法获取文件信息")
                    return False
                filename = file_info.get('fileName', f'file_{file_id}')
            save_path = os.path.join(save_path, filename)
        
        try:
            response = self.session.get(download_url, stream=True)
            if response.status_code == 200:
                os.makedirs(os.path.dirname(save_path), exist_ok=True)
                with open(save_path, 'wb') as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                self.logger.info(f"文件下载成功: {save_path}")
                return True
            else:
                self.logger.error(f"下载失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"下载文件失败: {e}")
        
        return False
    
    def make_dir(self, parent_id: str, folder_name: str) -> Optional[str]:
        """创建文件夹
        
        Args:
            parent_id: 父文件夹ID
            folder_name: 文件夹名称
        
        Returns:
            str: 创建成功返回文件夹ID，失败返回None
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        if parent_id is None:
            parent_id = "-11"  # 根目录
        
        url = "https://api.cloud.189.cn/createFolder.action"
        params = {
            'parentFolderId': parent_id,
            'folderName': folder_name
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "POST")
        
        try:
            response = self.session.post(url, data=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('res_code') == 0 or result.get('resCode') == 0:
                    folder_id = result.get('id')
                    self.logger.info(f"文件夹创建成功: {folder_name} (ID: {folder_id})")
                    return folder_id
                else:
                    self.logger.error(f"创建文件夹失败: {result.get('res_message', result.get('resMessage'))}")
        except Exception as e:
            self.logger.error(f"创建文件夹请求失败: {e}")
        
        return None
    
    def calculate_file_md5(self, file_data: Union[str, bytes, BinaryIO]) -> str:
        """计算文件MD5值
        
        Args:
            file_data: 文件数据（文件路径、字节数据或文件对象）
        
        Returns:
            str: MD5值（小写）
        """
        hash_md5 = hashlib.md5()
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
        elif isinstance(file_data, bytes):
            # 字节数据
            hash_md5.update(file_data)
        elif hasattr(file_data, 'read'):
            # 文件对象
            current_pos = file_data.tell() if hasattr(file_data, 'tell') else 0
            if hasattr(file_data, 'seek'):
                file_data.seek(0)
            
            for chunk in iter(lambda: file_data.read(4096), b""):
                hash_md5.update(chunk)
            
            # 恢复原始位置
            if hasattr(file_data, 'seek'):
                file_data.seek(current_pos)
        else:
            raise ValueError("不支持的文件数据类型")
        
        return hash_md5.hexdigest().lower()
    
    def calculate_slice_size(self, file_size: int) -> int:
        """计算分片大小
        
        Args:
            file_size: 文件大小（字节）
        
        Returns:
            int: 分片大小（字节）
        """
        if file_size <= 1024 * 1024 * 10:  # 10MB以下
            return 1024 * 1024  # 1MB
        elif file_size <= 1024 * 1024 * 100:  # 100MB以下
            return 1024 * 1024 * 5  # 5MB
        else:
            return 1024 * 1024 * 10  # 10MB
    
    def calculate_slice_md5(self, file_data: Union[str, bytes, BinaryIO], slice_size: int) -> List[str]:
        """计算文件分片MD5值
        
        Args:
            file_data: 文件数据
            slice_size: 分片大小
        
        Returns:
            List[str]: 分片MD5值列表
        """
        slice_md5_list = []
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, "rb") as f:
                while True:
                    chunk = f.read(slice_size)
                    if not chunk:
                        break
                    slice_md5_list.append(hashlib.md5(chunk).hexdigest().lower())
        elif isinstance(file_data, bytes):
            # 字节数据
            for i in range(0, len(file_data), slice_size):
                chunk = file_data[i:i + slice_size]
                slice_md5_list.append(hashlib.md5(chunk).hexdigest().lower())
        elif hasattr(file_data, 'read'):
            # 文件对象
            current_pos = file_data.tell() if hasattr(file_data, 'tell') else 0
            if hasattr(file_data, 'seek'):
                file_data.seek(0)
            
            while True:
                chunk = file_data.read(slice_size)
                if not chunk:
                    break
                slice_md5_list.append(hashlib.md5(chunk).hexdigest().lower())
            
            # 恢复原始位置
            if hasattr(file_data, 'seek'):
                file_data.seek(current_pos)
        else:
            raise ValueError("不支持的文件数据类型")
        
        return slice_md5_list